﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using Epona.Domain;
using Epona.Domain.Query.Impl;

namespace Epona.Domain.Query
{
    public class CriteriaQuery
    {
        public Type EntityType { private set; get; }
        public int FirstResult { get; set; }
        public int MaxResults { get; set; }
        public OrderSettings OrderSettings { get; private set; }

        private readonly IRepository repository;
        
        private ICriterionBuilder criterionBuilder=new CriteriaBuilder();
        public IQueryCriterion QueryCriterion { get; private set; }

        public CriteriaQuery(IRepository repository, Type type)
        {
            this.repository = repository;
            this.EntityType = type;
            QueryCriterion = criterionBuilder.Empty();
        }

        public string GetQueryString()
        {
            string result = string.Format("select distinct({0}) from {1} as {2}",
                                        QueryCriterion.ROOT_ALIAS, EntityType.Name, QueryCriterion.ROOT_ALIAS);
            if (string.IsNullOrEmpty(QueryCriterion.ToQueryString()))
            {
                result += " where " + QueryCriterion.ToQueryString();
            }
            result += GetOrderClause();
            return result;
        }

        private string GetOrderClause()
        {
            Dictionary<string, bool> orderBy = OrderSettings.Orderby;
            if (orderBy == null) return "";
            IList<string> elements = new List<string>();
            foreach (KeyValuePair<string, bool> each in orderBy)
            {
                elements.Add(QueryCriterion.ROOT_ALIAS + "." + each.Key + (each.Value ? " asc" : " desc"));
            }
            return "order by " + string.Join(", ", elements);
        }

        public NamedParameters GetParameters()
        {
            return QueryCriterion.GetParameters();
        }

        /// <summary>
        /// 添加一个“属性名 = 属性值”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery Eq(string propName, object value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Eq(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性名 != 属性值”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery NotEq(string propName, object value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotEq(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性名 大于 属性值”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery Gt(string propName, IComparable value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Gt(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性名 大于或等于 属性值”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery Ge(string propName, IComparable value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Ge(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性名 小于 属性值”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery Lt(string propName, IComparable value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Lt(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性名 小于或等于 属性值”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery Le(string propName, IComparable value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Le(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性1 等于 属性2”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="otherProp"></param>
        /// <returns></returns>
        public CriteriaQuery EqProp(string propName, string otherProp)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.EqProp(propName, otherProp));
            return this;
        }

        /// <summary>
        /// 添加一个“属性1 不等于 属性2”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="otherProp"></param>
        /// <returns></returns>
        public CriteriaQuery NotEqProp(string propName, string otherProp)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotEqProp(propName, otherProp));
            return this;
        }

        /// <summary>
        /// 添加一个“属性1 大于 属性2”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="otherProp"></param>
        /// <returns></returns>
        public CriteriaQuery GtProp(string propName, string otherProp)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.GtProp(propName, otherProp));
            return this;
        }

        /// <summary>
        /// 添加一个“属性1 大于或等于 属性2”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="otherProp"></param>
        /// <returns></returns>
        public CriteriaQuery GeProp(string propName, string otherProp)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.GeProp(propName, otherProp));
            return this;
        }

        /// <summary>
        /// 添加一个“属性1 小于 属性2”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="otherProp"></param>
        /// <returns></returns>
        public CriteriaQuery LtProp(string propName, string otherProp)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.LtProp(propName, otherProp));
            return this;
        }

        /// <summary>
        /// 添加一个“属性1 小于或等于 属性2”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="otherProp"></param>
        /// <returns></returns>
        public CriteriaQuery LeProp(string propName, string otherProp)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.LeProp(propName, otherProp));
            return this;
        }

        /// <summary>
        /// 添加一个“集合属性的结果数量 等于 size”的查询条件。例如Order对象有个类型为List(OrderItem)的
        /// 集合属性items，我们要查询订购了5种物品的订单，则propName为items，size为5. 
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public CriteriaQuery SizeEq(string propName, int size)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.SizeEq(propName, size));
            return this;
        }

        /// <summary>
        /// 添加一个“集合属性的结果数量 不等于 size”的查询条件。例如Order对象有个类型为List(OrderItem)的
        /// 集合属性items，我们要查询订购了不是5种物品的订单，则propName为items，size为5. 
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public CriteriaQuery SizeNotEq(string propName, int size)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.SizeNotEq(propName, size));
            return this;
        }

        /// <summary>
        /// 添加一个“集合属性的结果数量 大于 size”的查询条件。例如Order对象有个类型为List(OrderItem)的
        /// 集合属性items，我们要查询订购了超过5种物品的订单，则propName为items，size为5. 
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public CriteriaQuery SizeGt(string propName, int size)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.SizeGt(propName, size));
            return this;
        }

        /// <summary>
        /// 添加一个“集合属性的结果数量 大于或等于 size”的查询条件。例如Order对象有个类型为List(OrderItem)的
        /// 集合属性items，我们要查询订购了超过5种物品的订单，则propName为items，size为5. 
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public CriteriaQuery SizeGe(string propName, int size)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.SizeGe(propName, size));
            return this;
        }

        /// <summary>
        /// 添加一个“集合属性的结果数量 小于 size”的查询条件。例如Order对象有个类型为List(OrderItem)的
        /// 集合属性items，我们要查询订购了超过5种物品的订单，则propName为items，size为5. 
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public CriteriaQuery SizeLt(string propName, int size)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.SizeLt(propName, size));
            return this;
        }

        /// <summary>
        /// 添加一个“集合属性的结果数量 小于或等于 size”的查询条件。例如Order对象有个类型为List(OrderItem)的
        /// 集合属性items，我们要查询订购了超过5种物品的订单，则propName为items，size为5. 
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="size"></param>
        /// <returns></returns>
        public CriteriaQuery SizeLe(string propName, int size)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.SizeLe(propName, size));
            return this;
        }

        /// <summary>
        /// 添加一个“属性包含指定文本”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery ContainsText(string propName, string value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.ContainsText(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性以指定文本开头”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery StartsWithText(string propName, string value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.StartsWithText(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值包含在指定的集合内”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery In(string propName, ICollection value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.In(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值包含在指定的数组内”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery In(string propName, object[] value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.In(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值不包含在指定的集合内”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery NotIn(string propName, ICollection value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotIn(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值不包含在指定的数组内”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public CriteriaQuery NotIn(string propName, object[] value)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotIn(propName, value));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值介于两个值之间（包含左右边界）”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <param name="from"></param>
        /// <param name="to"></param>
        /// <returns></returns>
        public CriteriaQuery Between(string propName, IComparable from, IComparable to)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Between(propName, from, to));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值是Null”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery IsNull(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.IsNull(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值不是Null”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery NotNull(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotNull(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“集合类型属性值为空集合”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery IsEmpty(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.IsEmpty(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“集合类型属性值不是空集合”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery NotEmpty(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotEmpty(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值为true”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery IsTrue(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.IsTrue(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值为false”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery IsFalse(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.IsFalse(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值为空白，即Null或空字符串”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery IsBlank(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.IsBlank(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“属性值非空”的查询条件
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery NotBlank(string propName)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.NotBlank(propName));
            return this;
        }

        /// <summary>
        /// 添加一个“取反”的查询条件
        /// </summary>
        /// <param name="otherCriterion"></param>
        /// <returns></returns>
        public CriteriaQuery Not(IQueryCriterion otherCriterion)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Not(otherCriterion));
            return this;
        }

        /// <summary>
        /// 添加一个“与”的查询条件，即同时符合指定的几个查询条件
        /// </summary>
        /// <param name="queryCriterions"></param>
        /// <returns></returns>
        public CriteriaQuery And(params IQueryCriterion[] queryCriterions)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.And(queryCriterions));
            return this;
        }

        /// <summary>
        /// 添加一个“或”的查询条件，即符合指定的几个查询条件之一
        /// </summary>
        /// <param name="queryCriterions"></param>
        /// <returns></returns>
        public CriteriaQuery Or(params IQueryCriterion[] queryCriterions)
        {
            QueryCriterion = QueryCriterion.And(criterionBuilder.Or(queryCriterions));
            return this;
        }

        /// <summary>
        /// 按指定的属性的升序对结果集排序
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery Asc(string propName)
        {
            OrderSettings.asc(propName);
            return this;
        }

        /// <summary>
        /// 按指定的属性的降序对结果集排序
        /// </summary>
        /// <param name="propName"></param>
        /// <returns></returns>
        public CriteriaQuery Desc(string propName)
        {
            OrderSettings.desc(propName);
            return this;
        }

        /// <summary>
        /// 返回查询结果列表
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public IList<T> List<T>() where T : IEntity
        {
            return repository.Find<T>(this);
        }

        /// <summary>
        /// 返回单条查询结果
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T SingleResult<T>() where T : IEntity
        {
            return repository.GetSingleResult<T>(this);
        }
    }
}
